home *** CD-ROM | disk | FTP | other *** search
/ Software of the Month Club 2000 October / Software of the Month - Ultimate Collection Shareware 277.iso / pc / PROGRAMS / UTILITY / WINLINUX / DATA1.CAB / programs_-_kernel_source / IPC / MSG.C < prev    next >
C/C++ Source or Header  |  1999-09-17  |  12KB  |  494 lines

  1. /*
  2.  * linux/ipc/msg.c
  3.  * Copyright (C) 1992 Krishna Balasubramanian 
  4.  *
  5.  * Removed all the remaining kerneld mess
  6.  * Catch the -EFAULT stuff properly
  7.  * Use GFP_KERNEL for messages as in 1.2
  8.  * Fixed up the unchecked user space derefs
  9.  * Copyright (C) 1998 Alan Cox & Andi Kleen
  10.  *
  11.  */
  12.  
  13. #include <linux/malloc.h>
  14. #include <linux/msg.h>
  15. #include <linux/interrupt.h>
  16. #include <linux/smp_lock.h>
  17. #include <linux/init.h>
  18.  
  19. #include <asm/uaccess.h>
  20.  
  21. extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
  22.  
  23. static void freeque (int id);
  24. static int newque (key_t key, int msgflg);
  25. static int findkey (key_t key);
  26.  
  27. static struct msqid_ds *msgque[MSGMNI];
  28. static int msgbytes = 0;
  29. static int msghdrs = 0;
  30. static unsigned short msg_seq = 0;
  31. static int used_queues = 0;
  32. static int max_msqid = 0;
  33. static struct wait_queue *msg_lock = NULL;
  34.  
  35. void __init msg_init (void)
  36. {
  37.     int id;
  38.     
  39.     for (id = 0; id < MSGMNI; id++) 
  40.         msgque[id] = (struct msqid_ds *) IPC_UNUSED;
  41.     msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
  42.     msg_lock = NULL;
  43.     return;
  44. }
  45.  
  46. static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
  47. {
  48.     int id;
  49.     struct msqid_ds *msq;
  50.     struct ipc_perm *ipcp;
  51.     struct msg *msgh;
  52.     long mtype;
  53.     
  54.     if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)
  55.         return -EINVAL;
  56.     if (get_user(mtype, &msgp->mtype))
  57.         return -EFAULT; 
  58.     if (mtype < 1)
  59.         return -EINVAL;
  60.     id = (unsigned int) msqid % MSGMNI;
  61.     msq = msgque [id];
  62.     if (msq == IPC_UNUSED || msq == IPC_NOID)
  63.         return -EINVAL;
  64.     ipcp = &msq->msg_perm; 
  65.  
  66.  slept:
  67.     if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) 
  68.         return -EIDRM;
  69.  
  70.     if (ipcperms(ipcp, S_IWUGO)) 
  71.         return -EACCES;
  72.     
  73.     if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
  74.         if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
  75.             /* still no space in queue */
  76.             if (msgflg & IPC_NOWAIT)
  77.                 return -EAGAIN;
  78.             if (signal_pending(current))
  79.                 return -EINTR;
  80.             interruptible_sleep_on (&msq->wwait);
  81.             goto slept;
  82.         }
  83.     }
  84.     
  85.     /* allocate message header and text space*/ 
  86.     msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_KERNEL);
  87.     if (!msgh)
  88.         return -ENOMEM;
  89.     msgh->msg_spot = (char *) (msgh + 1);
  90.  
  91.     if (copy_from_user(msgh->msg_spot, msgp->mtext, msgsz))
  92.     {
  93.         kfree(msgh);
  94.         return -EFAULT;
  95.     }
  96.     
  97.     if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
  98.         || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
  99.         kfree(msgh);
  100.         return -EIDRM;
  101.     }
  102.  
  103.     msgh->msg_next = NULL;
  104.     msgh->msg_ts = msgsz;
  105.     msgh->msg_type = mtype;
  106.     msgh->msg_stime = CURRENT_TIME;
  107.  
  108.     if (!msq->msg_first)
  109.         msq->msg_first = msq->msg_last = msgh;
  110.     else {
  111.         msq->msg_last->msg_next = msgh;
  112.         msq->msg_last = msgh;
  113.     }
  114.     msq->msg_cbytes += msgsz;
  115.     msgbytes  += msgsz;
  116.     msghdrs++;
  117.     msq->msg_qnum++;
  118.     msq->msg_lspid = current->pid;
  119.     msq->msg_stime = CURRENT_TIME;
  120.     wake_up (&msq->rwait);
  121.     return 0;
  122. }
  123.  
  124. static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
  125. {
  126.     struct msqid_ds *msq;
  127.     struct ipc_perm *ipcp;
  128.     struct msg *tmsg, *leastp = NULL;
  129.     struct msg *nmsg = NULL;
  130.     int id;
  131.  
  132.     if (msqid < 0 || (long) msgsz < 0)
  133.         return -EINVAL;
  134.  
  135.     id = (unsigned int) msqid % MSGMNI;
  136.     msq = msgque [id];
  137.     if (msq == IPC_NOID || msq == IPC_UNUSED)
  138.         return -EINVAL;
  139.     ipcp = &msq->msg_perm; 
  140.  
  141.     /* 
  142.      *  find message of correct type.
  143.      *  msgtyp = 0 => get first.
  144.      *  msgtyp > 0 => get first message of matching type.
  145.      *  msgtyp < 0 => get message with least type must be < abs(msgtype).  
  146.      */
  147.     while (!nmsg) {
  148.         if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
  149.             return -EIDRM;
  150.         }
  151.         if (ipcperms (ipcp, S_IRUGO)) {
  152.             return -EACCES;
  153.         }
  154.  
  155.         if (msgtyp == 0) 
  156.             nmsg = msq->msg_first;
  157.         else if (msgtyp > 0) {
  158.             if (msgflg & MSG_EXCEPT) { 
  159.                 for (tmsg = msq->msg_first; tmsg; 
  160.                      tmsg = tmsg->msg_next)
  161.                     if (tmsg->msg_type != msgtyp)
  162.                         break;
  163.                 nmsg = tmsg;
  164.             } else {
  165.                 for (tmsg = msq->msg_first; tmsg; 
  166.                      tmsg = tmsg->msg_next)
  167.                     if (tmsg->msg_type == msgtyp)
  168.                         break;
  169.                 nmsg = tmsg;
  170.             }
  171.         } else {
  172.             for (leastp = tmsg = msq->msg_first; tmsg; 
  173.                  tmsg = tmsg->msg_next) 
  174.                 if (tmsg->msg_type < leastp->msg_type) 
  175.                     leastp = tmsg;
  176.             if (leastp && leastp->msg_type <= - msgtyp)
  177.                 nmsg = leastp;
  178.         }
  179.         
  180.         if (nmsg) { /* done finding a message */
  181.             if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
  182.                 return -E2BIG;
  183.             }
  184.             msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
  185.             if (nmsg ==  msq->msg_first)
  186.                 msq->msg_first = nmsg->msg_next;
  187.             else {
  188.                 for (tmsg = msq->msg_first; tmsg; 
  189.                      tmsg = tmsg->msg_next)
  190.                     if (tmsg->msg_next == nmsg) 
  191.                         break;
  192.                 tmsg->msg_next = nmsg->msg_next;
  193.                 if (nmsg == msq->msg_last)
  194.                     msq->msg_last = tmsg;
  195.             }
  196.             if (!(--msq->msg_qnum))
  197.                 msq->msg_last = msq->msg_first = NULL;
  198.             
  199.             msq->msg_rtime = CURRENT_TIME;
  200.             msq->msg_lrpid = current->pid;
  201.             msgbytes -= nmsg->msg_ts; 
  202.             msghdrs--; 
  203.             msq->msg_cbytes -= nmsg->msg_ts;
  204.             wake_up (&msq->wwait);
  205.             if (put_user (nmsg->msg_type, &msgp->mtype) ||
  206.                 copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz))
  207.                 msgsz = -EFAULT; 
  208.             kfree(nmsg);
  209.             return msgsz;
  210.         } else {  /* did not find a message */
  211.             if (msgflg & IPC_NOWAIT) {
  212.                 return -ENOMSG;
  213.             }
  214.             if (signal_pending(current)) {
  215.                 return -EINTR; 
  216.             }
  217.             interruptible_sleep_on (&msq->rwait);
  218.         }
  219.     } /* end while */
  220.     return -1;
  221. }
  222.  
  223. asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
  224. {
  225.     int ret;
  226.  
  227.     lock_kernel();
  228.     ret = real_msgsnd(msqid, msgp, msgsz, msgflg);
  229.     unlock_kernel();
  230.     return ret;
  231. }
  232.  
  233. asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
  234.     long msgtyp, int msgflg)
  235. {
  236.     int ret;
  237.  
  238.     lock_kernel();
  239.     ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg);
  240.     unlock_kernel();
  241.     return ret;
  242. }
  243.  
  244. static int findkey (key_t key)
  245. {
  246.     int id;
  247.     struct msqid_ds *msq;
  248.     
  249.     for (id = 0; id <= max_msqid; id++) {
  250.         while ((msq = msgque[id]) == IPC_NOID) 
  251.             interruptible_sleep_on (&msg_lock);
  252.         if (msq == IPC_UNUSED)
  253.             continue;
  254.         if (key == msq->msg_perm.key)
  255.             return id;
  256.     }
  257.     return -1;
  258. }
  259.  
  260. static int newque (key_t key, int msgflg)
  261. {
  262.     int id;
  263.     struct msqid_ds *msq;
  264.     struct ipc_perm *ipcp;
  265.  
  266.     for (id = 0; id < MSGMNI; id++) 
  267.         if (msgque[id] == IPC_UNUSED) {
  268.             msgque[id] = (struct msqid_ds *) IPC_NOID;
  269.             goto found;
  270.         }
  271.     return -ENOSPC;
  272.  
  273. found:
  274.     msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
  275.     if (!msq) {
  276.         msgque[id] = (struct msqid_ds *) IPC_UNUSED;
  277.         wake_up (&msg_lock);
  278.         return -ENOMEM;
  279.     }
  280.     ipcp = &msq->msg_perm;
  281.     ipcp->mode = (msgflg & S_IRWXUGO);
  282.     ipcp->key = key;
  283.     ipcp->cuid = ipcp->uid = current->euid;
  284.     ipcp->gid = ipcp->cgid = current->egid;
  285.     msq->msg_perm.seq = msg_seq;
  286.     msq->msg_first = msq->msg_last = NULL;
  287.     msq->rwait = msq->wwait = NULL;
  288.     msq->msg_cbytes = msq->msg_qnum = 0;
  289.     msq->msg_lspid = msq->msg_lrpid = 0;
  290.     msq->msg_stime = msq->msg_rtime = 0;
  291.     msq->msg_qbytes = MSGMNB;
  292.     msq->msg_ctime = CURRENT_TIME;
  293.     if (id > max_msqid)
  294.         max_msqid = id;
  295.     msgque[id] = msq;
  296.     used_queues++;
  297.     wake_up (&msg_lock);
  298.     return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
  299. }
  300.  
  301. asmlinkage int sys_msgget (key_t key, int msgflg)
  302. {
  303.     int id, ret = -EPERM;
  304.     struct msqid_ds *msq;
  305.     
  306.     lock_kernel();
  307.     if (key == IPC_PRIVATE) 
  308.         ret = newque(key, msgflg);
  309.     else if ((id = findkey (key)) == -1) { /* key not used */
  310.         if (!(msgflg & IPC_CREAT))
  311.             ret = -ENOENT;
  312.         else
  313.             ret = newque(key, msgflg);
  314.     } else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {
  315.         ret = -EEXIST;
  316.     } else {
  317.         msq = msgque[id];
  318.         if (msq == IPC_UNUSED || msq == IPC_NOID)
  319.             ret = -EIDRM;
  320.         else if (ipcperms(&msq->msg_perm, msgflg))
  321.             ret = -EACCES;
  322.         else
  323.             ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id;
  324.     }
  325.     unlock_kernel();
  326.     return ret;
  327.  
  328. static void freeque (int id)
  329. {
  330.     struct msqid_ds *msq = msgque[id];
  331.     struct msg *msgp, *msgh;
  332.  
  333.     msq->msg_perm.seq++;
  334.     msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI); /* increment, but avoid overflow */
  335.     msgbytes -= msq->msg_cbytes;
  336.     if (id == max_msqid)
  337.         while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
  338.     msgque[id] = (struct msqid_ds *) IPC_UNUSED;
  339.     used_queues--;
  340.     while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) {
  341.         wake_up (&msq->rwait); 
  342.         wake_up (&msq->wwait);
  343.         schedule(); 
  344.     }
  345.     for (msgp = msq->msg_first; msgp; msgp = msgh ) {
  346.         msgh = msgp->msg_next;
  347.         msghdrs--;
  348.         kfree(msgp);
  349.     }
  350.     kfree(msq);
  351. }
  352.  
  353. asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
  354. {
  355.     int id, err = -EINVAL;
  356.     struct msqid_ds *msq;
  357.     struct msqid_ds tbuf;
  358.     struct ipc_perm *ipcp;
  359.     
  360.     lock_kernel();
  361.     if (msqid < 0 || cmd < 0)
  362.         goto out;
  363.     err = -EFAULT;
  364.     switch (cmd) {
  365.     case IPC_INFO: 
  366.     case MSG_INFO: 
  367.         if (!buf)
  368.             goto out;
  369.     { 
  370.         struct msginfo msginfo;
  371.         msginfo.msgmni = MSGMNI;
  372.         msginfo.msgmax = MSGMAX;
  373.         msginfo.msgmnb = MSGMNB;
  374.         msginfo.msgmap = MSGMAP;
  375.         msginfo.msgpool = MSGPOOL;
  376.         msginfo.msgtql = MSGTQL;
  377.         msginfo.msgssz = MSGSSZ;
  378.         msginfo.msgseg = MSGSEG;
  379.         if (cmd == MSG_INFO) {
  380.             msginfo.msgpool = used_queues;
  381.             msginfo.msgmap = msghdrs;
  382.             msginfo.msgtql = msgbytes;
  383.         }
  384.  
  385.         err = -EFAULT; 
  386.         if (copy_to_user (buf, &msginfo, sizeof(struct msginfo)))
  387.             goto out; 
  388.         err = max_msqid;
  389.         goto out;
  390.     }
  391.     case MSG_STAT:
  392.         if (!buf)
  393.             goto out;
  394.         err = -EINVAL;
  395.         if (msqid > max_msqid)
  396.             goto out;
  397.         msq = msgque[msqid];
  398.         if (msq == IPC_UNUSED || msq == IPC_NOID)
  399.             goto out;
  400.         err = -EACCES;
  401.         if (ipcperms (&msq->msg_perm, S_IRUGO))
  402.             goto out;
  403.         id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid;
  404.         tbuf.msg_perm   = msq->msg_perm;
  405.         tbuf.msg_stime  = msq->msg_stime;
  406.         tbuf.msg_rtime  = msq->msg_rtime;
  407.         tbuf.msg_ctime  = msq->msg_ctime;
  408.         tbuf.msg_cbytes = msq->msg_cbytes;
  409.         tbuf.msg_qnum   = msq->msg_qnum;
  410.         tbuf.msg_qbytes = msq->msg_qbytes;
  411.         tbuf.msg_lspid  = msq->msg_lspid;
  412.         tbuf.msg_lrpid  = msq->msg_lrpid;
  413.         err = -EFAULT;
  414.         if (copy_to_user (buf, &tbuf, sizeof(*buf)))
  415.             goto out; 
  416.         err = id;
  417.         goto out;
  418.     case IPC_SET:
  419.         if (!buf)
  420.             goto out;
  421.         err = -EFAULT; 
  422.         if (!copy_from_user (&tbuf, buf, sizeof (*buf)))
  423.             err = 0; 
  424.         break;
  425.     case IPC_STAT:
  426.         if (!buf)
  427.             goto out;
  428.         break;
  429.     }
  430.  
  431.     id = (unsigned int) msqid % MSGMNI;
  432.     msq = msgque [id];
  433.     err = -EINVAL;
  434.     if (msq == IPC_UNUSED || msq == IPC_NOID)
  435.         goto out;
  436.     err = -EIDRM;
  437.     if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
  438.         goto out;
  439.     ipcp = &msq->msg_perm;
  440.  
  441.     switch (cmd) {
  442.     case IPC_STAT:
  443.         err = -EACCES;
  444.         if (ipcperms (ipcp, S_IRUGO))
  445.             goto out;
  446.         tbuf.msg_perm   = msq->msg_perm;
  447.         tbuf.msg_stime  = msq->msg_stime;
  448.         tbuf.msg_rtime  = msq->msg_rtime;
  449.         tbuf.msg_ctime  = msq->msg_ctime;
  450.         tbuf.msg_cbytes = msq->msg_cbytes;
  451.         tbuf.msg_qnum   = msq->msg_qnum;
  452.         tbuf.msg_qbytes = msq->msg_qbytes;
  453.         tbuf.msg_lspid  = msq->msg_lspid;
  454.         tbuf.msg_lrpid  = msq->msg_lrpid;
  455.         err = -EFAULT;
  456.         if (!copy_to_user (buf, &tbuf, sizeof (*buf)))
  457.             err = 0;
  458.         goto out;
  459.     case IPC_SET:
  460.         err = -EPERM;
  461.         if (current->euid != ipcp->cuid && 
  462.             current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
  463.             /* We _could_ check for CAP_CHOWN above, but we don't */
  464.             goto out;
  465.         if (tbuf.msg_qbytes > MSGMNB && !capable(CAP_SYS_RESOURCE))
  466.             goto out;
  467.         msq->msg_qbytes = tbuf.msg_qbytes;
  468.         ipcp->uid = tbuf.msg_perm.uid;
  469.         ipcp->gid =  tbuf.msg_perm.gid;
  470.         ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | 
  471.             (S_IRWXUGO & tbuf.msg_perm.mode);
  472.         msq->msg_ctime = CURRENT_TIME;
  473.         err = 0;
  474.         goto out;
  475.     case IPC_RMID:
  476.         err = -EPERM;
  477.         if (current->euid != ipcp->cuid && 
  478.             current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
  479.             goto out;
  480.  
  481.         freeque (id); 
  482.         err = 0;
  483.         goto out;
  484.     default:
  485.         err = -EINVAL;
  486.         goto out;
  487.     }
  488. out:
  489.     unlock_kernel();
  490.     return err;
  491. }
  492.  
  493.